package com.mainbo.modular.excel.impl;

import cn.hutool.core.io.resource.ClassPathResource;
import cn.stylefeng.roses.core.util.HttpContext;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.mainbo.core.util.EncryptPassword;
import com.mainbo.core.util.ExcelCascadeRecord;
import com.mainbo.core.util.ExcelUtils;
import com.mainbo.core.util.ExcelUtils.ExcelType;
import com.mainbo.modular.excel.ClassUserBatchStudentService;
import com.mainbo.modular.excel.ExcelBatchService;
import com.mainbo.modular.jms.JmsService;
import com.mainbo.modular.jms.MessageTypes;
import com.mainbo.modular.jms.model.ClassUserMessage;
import com.mainbo.modular.system.dao.PtAccountMapper;
import com.mainbo.modular.system.dao.PtAreaMapper;
import com.mainbo.modular.system.dao.PtClassMapper;
import com.mainbo.modular.system.dao.PtOrgMapper;
import com.mainbo.modular.system.model.*;
import com.mainbo.modular.system.model.meta.MetaUtils;
import com.mainbo.modular.system.model.meta.vo.Meta;
import com.mainbo.modular.system.service.IPtClassUserService;
import com.mainbo.modular.system.service.StudentService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author moshang
 * @date 2020-03-08
 **/
@Service
public class ClassUserBatchStudentServiceImpl extends ExcelBatchService
        implements ClassUserBatchStudentService {

    private static final Logger logger = LoggerFactory
            .getLogger(ClassUserBatchStudentServiceImpl.class);

    @Resource
    private PtOrgMapper orgMapper;
    @Resource
    private PtAreaMapper areaMapper;
    @Resource
    private PtClassMapper classMapper;
    @Resource
    private IPtClassUserService classUserService;
    @Resource
    private StudentService studentService;
    @Resource
    JmsService jmsService;
    @Resource
    PtAccountMapper accountMapper;
    /**
     * 用户模板头部所在行
     */
    private final Integer headline = 3;

    /**
     * 下载批量关联学生的模板
     *
     * @param orgId
     *          机构ID
     * @param schoolYear
     *          学年ID
     */
    @SuppressWarnings("unused")
    @Override
    public void downloadBatchManageStudentTemplate(String orgId,
                                                   HttpServletResponse response, Integer schoolYear) throws IOException {
        Workbook workbook = null;
        String fileName = "批量添加" + schoolYear + "学年学生";
        String filepath = "";
        filepath = "/registertemplate/batchmanagetemplate_student.xls";
        ClassPathResource resource = new ClassPathResource(filepath);
        InputStream inputStream = resource.getStream();
        workbook = ExcelUtils.createWorkBook(inputStream,ExcelType.EXL_2003);
        if (workbook == null) {
            returnErrMsg(filepath, response);
            return;
        }
        PtOrg org = orgMapper.selectById(orgId);
        String orgStr = "学校名称：" + org.getName();
        PtArea area1 = areaMapper.selectById(org.getAreaId());
        if (area1.getPid().intValue() != 0) {
            PtArea area2 = areaMapper.selectById(area1.getPid());
            orgStr = "省(市)：" + area2.getAreaName() + "     区(县)："
                    + area1.getAreaName() + "     " + orgStr;
        } else {
            orgStr = "区(县)：" + area1.getAreaName() + "     " + orgStr;
        }
        Map<String, Object> params = new HashMap<String, Object>();
        Sheet sheet = workbook.getSheet("Sheet");
        params.put(TITLE_PARAMS, new HashSet<ExcelTitle>());
        params.put("schoolYear", schoolYear);
        sheet.getRow(2).getCell(0).setCellValue(orgStr);
        Map<ExcelTitle, Column> headers = parseExcelHeader(sheet, params);
        initSheetTeplate(workbook, sheet, org, headers, params);
        try {
            if (workbook != null) {
                String ext = filepath.substring(filepath.lastIndexOf("."));
                response.reset();
                response.setContentType("application/x-msdownload");
                response.setHeader("Content-Disposition", "attachment; filename="
                        + new String(fileName.getBytes("gb2312"), "ISO-8859-1") + ext);
                workbook.write(response.getOutputStream());
            } else {
                response.getWriter().write("模板文件不存在！");
            }
        } catch (Exception e) {
            logger.error("", e);

        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    logger.error("close failed", e);
                }
            }
        }
    }

    private void returnErrMsg(String path, HttpServletResponse response) {
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        StringBuilder rb = new StringBuilder(path).append(" 不存在!");
        try {
            response.getWriter().write(rb.toString());
        } catch (IOException e) {
            logger.error("", e);
        }
    }

    /**
     * 初始化学校模板
     *
     * @param workbook
     *
     */
    private void initSheetTeplate(Workbook workbook, Sheet sheet,
                                  PtOrg org, Map<ExcelTitle, Column> headers,
                                  Map<String, Object> params) {
        Column cn = null;
        List<Meta> gradeMetas = getAllgradeMetaList(org);
        // 年级id name 映射
        Map<Integer, String> gradeMap = new HashMap<Integer, String>();
        Map<String, Object> nameMap = new LinkedHashMap<String, Object>();// 年级下所有班级映射
        for (Meta meta : gradeMetas) {
            gradeMap.put(meta.getId(), meta.getName());
            nameMap.put(meta.getName(), new ArrayList<String>());
        }
        Integer gradeClumnIndex = null;
        if ((cn = headers.get(ExcelHeader.GRADE)) != null) {
            gradeClumnIndex = cn.getIndex();
        }
        if ((cn = headers.get(ExcelHeader.CLASS)) != null) {
            int clumnIndex = cn.getIndex();
            EntityWrapper<PtClass> classModel = new EntityWrapper<>();
            classModel.eq("org_id",org.getId());
            classModel.eq("enable",1);
            classModel.eq("school_year",(Integer) params.get("schoolYear"));
            classModel.orderAsc(Collections.singleton("sort"));
            List<PtClass> classList = classMapper.selectList(classModel);
            String[] classnames = new String[classList.size() + 1];
            int i = 0;
            for (PtClass sch : classList) {
                if (gradeMap.get(sch.getGradeId()) != null) {
                    @SuppressWarnings("unchecked")
                    List<String> clss = (List<String>) nameMap.get(gradeMap.get(sch
                            .getGradeId()));
                    if (clss != null) {
                        clss.add(sch.getName());
                    } else {
                        List<String> names = new ArrayList<String>();
                        names.add(sch.getName());
                        nameMap.put(gradeMap.get(sch.getGradeId()), names);
                    }
                }
                classnames[i++] = sch.getName();
            }
            classnames[i] = "无";
            if (gradeClumnIndex != null) {
                ExcelCascadeRecord createCascadeRecord = ExcelUtils
                        .createCascadeRecord(workbook, nameMap);// 创建数据字典第一行
                DataValidation setDataValidationName = ExcelUtils.createDataValidation(
                        sheet, createCascadeRecord.getName(), headline + 1, 500,
                        gradeClumnIndex, gradeClumnIndex);
                sheet.addValidationData(setDataValidationName);
                for (int start = headline + 1; start < 500; start++) {
                    int relationRow = start + 1;
                    DataValidation setDataValidationByName = ExcelUtils
                            .createDataValidation(sheet,
                                    "INDIRECT($" + ExcelUtils.indexToColumn(gradeClumnIndex + 1)
                                            + "$" + relationRow + ")", start, start, clumnIndex,
                                    clumnIndex);
                    sheet.addValidationData(setDataValidationByName);
                }

            }
        }
    }

    /**
     * 获取学校的所有班级
     */
    private List<Meta> getAllgradeMetaList(PtOrg org) {
        List<Meta> phaseList = MetaUtils.getOrgTypeProvider().listAllPhase(
                org.getSchoolings());
        Map<Integer, List<Meta>> phasegradeMap = MetaUtils.getOrgTypeProvider()
                .listPhaseXzGradeMap(org.getSchoolings());
        List<Meta> gradeMetaList = new ArrayList<Meta>();
        for (Meta phaseMeta : phaseList) {
            gradeMetaList.addAll(phasegradeMap.get(phaseMeta.getId()));
        }
        return gradeMetaList;
    }

    /**
     * 批量导入班级和学生的关联
     *
     * @param orgId
     *          机构ID
     * @param file
     *          文件
     * @param schoolYear
     *          学年
     */
    @SuppressWarnings("unchecked")
    @Override
    public StringBuilder batchImportStudent(String orgId, MultipartFile file,
                                            Integer schoolYear) {
        Map<String, Object> params = new HashMap<String, Object>();
        StringBuilder resultStr = new StringBuilder(); // 反馈信息
        params.put("orgId", orgId);
        params.put("schoolYear", schoolYear);
        try {
            resultStr.append("<strong>==》开始导入数据，请等待...</strong><br>");
            this.importData(file.getInputStream(),
                    ExcelType.parseExcelType(file.getOriginalFilename()), params,
                    resultStr);
            resultStr.append("<br><strong>《==数据导入结束。</strong>");
            // 开始批量保存
            ClassUserMessage classUserVo = new ClassUserMessage();
            classUserVo.setOrgId(orgId);
            classUserVo.setType(PtClassUser.TYPE_STUDENT);
            classUserVo.setStartTime(new Date());
            classUserVo.setSchoolYear(schoolYear);
            classUserVo.setMessageType(MessageTypes.CLASS_STUDENT_IMPORT.name());
            Map<String, List<PtClassUser>> classIdclassUserListMap = (Map<String, List<PtClassUser>>) params
                    .get("classId_classUserListMap");

            for (String classId : classIdclassUserListMap.keySet()) {
                ClassUserMessage cuv = new ClassUserMessage();
                cuv.setClassId(classId);
                cuv.setOrgId(orgId);
                cuv.setClassUserList(classIdclassUserListMap.get(classId));
                cuv.setSchoolYear(schoolYear);
                classUserService.saveOrUpdateClassUser(cuv, PtClassUser.TYPE_STUDENT);
            }
            classUserVo.setEndTime(new Date());
//            LoggerUtils.insertLogger(LoggerModule.ORGMANAGE,
//                    "组织机构管理——班级管理——批量导入学生，学校ID:{}", orgId);
            try {
                if (classIdclassUserListMap.size() > 0) {
                    classUserVo.setMessageType(MessageTypes.CLASS_STUDENT_IMPORT.name());
                    classUserVo.setClassIds((Set<String>) params.get("classIds"));
                    jmsService.sendMessage(classUserVo);
                }
            } catch (Exception e) {
                logger.error("组织机构管理——班级管理——批量导入学生——消息发送失败", e);
            }
        } catch (Exception e) {
            resultStr.append("批量导入失败：").append(e.getMessage());
            logger.error("read excel file failed", e);
        }

        return resultStr;
    }

    enum ExcelHeader implements ExcelTitle {
        GRADE(true, 32, "年级"), CLASS(true, 32, "班级"), STUDENT(true, 32, "学生姓名");

        private String[] mapNames;

        private boolean required = false;

        private Integer size = null;

        private ExcelHeader(String... header) {
            this.mapNames = header;
        }

        private ExcelHeader(boolean required, String... header) {
            this.mapNames = header;
            this.required = required;
        }

        private ExcelHeader(Integer size, String... header) {
            this.mapNames = header;
            this.size = size;
        }

        private ExcelHeader(boolean required, Integer size, String... header) {
            this.mapNames = header;
            this.required = required;
            this.size = size;
        }

        @Override
        public List<String> getMapNames() {
            return Arrays.asList(mapNames);
        }

        @Override
        public boolean isRequired() {
            return required;
        }

        @Override
        public int length() {
            return size;
        }
    }

    @Override
    protected void beforeSheetParse(Sheet sheet, Map<String, Object> params,
                                    StringBuilder returnMsg) {
        if (params != null) {
            // 年级班级匹配
            List<Meta> gradeMetaList = getAllGradeMetaList(String.valueOf(params
                    .get("orgId")));
            Map<Integer, Map<String, String>> gradeClassMaps = new HashMap<Integer, Map<String, String>>();
            Map<String, Integer> gradeMetaMap = new HashMap<String, Integer>();
            params.put("classIds", new HashSet<String>());
            for (Meta gradeMeta : gradeMetaList) {
                EntityWrapper<PtClass> model = new EntityWrapper<>();
                model.eq("grade_id",gradeMeta.getId());
                model.eq("org_id",String.valueOf(params.get("orgId")));
                model.eq("enable",1);
                List<PtClass> classList = classMapper.selectList(model);
                Map<String, String> map = new HashMap<String, String>();
                for (PtClass classInfo : classList) {
                    map.put(classInfo.getId(), classInfo.getName());
                }
                gradeClassMaps.put(gradeMeta.getId(), map);
                gradeMetaMap.put(gradeMeta.getName(), gradeMeta.getId());
            }
            params.put("gradeClass", gradeClassMaps);
            params.put("gradeMetaList", gradeMetaList);
            params.put("gradeMetaMap", gradeMetaMap);
            params.put(
                    "lookUpUser",
                    lookUpUser(String.valueOf(params.get("orgId")),
                            (Integer) params.get("schoolYear")));
            Map<String, List<PtClassUser>> classIdclassUserListMap = new HashMap<String, List<PtClassUser>>(); // map:班级id——其下学生集合
            params.put("classId_classUserListMap", classIdclassUserListMap);
            // 检查待导入的学生是否有重复的 map,key:学生姓名，value:行号
            Map<String, Integer> checkRepeatStudentMap = new HashMap<String, Integer>();
            params.put("checkRepeatStudentMap", checkRepeatStudentMap);
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    protected void parseRow(Map<ExcelTitle, String> rowValueMap,
                            Map<String, Object> params, Row row, StringBuilder sb) {
        if (row != null) {
            int i = row.getRowNum();
            Map<String, List<PtClassUser>> classIdclassUserListMap = (Map<String, List<PtClassUser>>) params
                    .get("classId_classUserListMap");
            String classId = getClassId(rowValueMap, params);
            if (classId == null) {
                sb.append("第" + (i + 1) + "行的班级不存在，忽略此行数据。<br>");
                logger.debug("第{}行的班级不存在，忽略此行数据。", i + 1);
                return;
            }
            // 数据校验
            if (!checkCulumn(i, rowValueMap, sb, params)) {
                return;
            }
            // 数据保存
            List<PtClassUser> classUserList = classIdclassUserListMap.get(classId);
            if (classUserList == null) {
                classUserList = new ArrayList<PtClassUser>();
                classIdclassUserListMap.put(classId, classUserList);
            }
            PtClassUser student = new PtClassUser();
            student.setUserName(rowValueMap.get(ExcelHeader.STUDENT));
            Map<String, String> lookUpUser = (Map<String, String>) params
                    .get("lookUpUser");
            student.setUserId(lookUpUser.get(student.getUserName()));
            student.setAccount(accountMapper.selectById(student.getUserId()).getAccount());
            student.setSchoolYear((Integer) params.get("schoolYear"));
            Set<String> clssIds = (Set<String>) params.get("classIds");
            clssIds.add(classId);
            classUserList.add(student);
        }
    }

    @SuppressWarnings("unchecked")
    private boolean checkCulumn(int i, Map<ExcelTitle, String> rowValueMap,
                                StringBuilder sb, Map<String, Object> params) {
        i++;
        Map<String, Integer> gradeMetaMap = (Map<String, Integer>) params
                .get("gradeMetaMap");
        // 校验空数据
        String gradeName = rowValueMap.get(ExcelHeader.GRADE);
        String className = rowValueMap.get(ExcelHeader.CLASS);
        String studentName = rowValueMap.get(ExcelHeader.STUDENT);
        if (StringUtils.isEmpty(gradeName) || StringUtils.isEmpty(className)) {
            sb.append("第" + (i) + "行必填项有空值，忽略此行数据。<br>");
            logger.debug("第{}行必填项有空值，忽略此行数据。", i);
            return false;
        } else if (StringUtils.isEmpty(studentName)) {
            sb.append("第" + (i) + "行" + ExcelHeader.STUDENT + "不能为空，忽略此行数据。<br>");
            logger.debug("第" + (i) + "行" + ExcelHeader.STUDENT + "不能为空，忽略此行数据。", i);
            return false;
        }
        // 校验年级班级是否对应
        Map<Integer, Map<String, String>> gradeClassMap = (Map<Integer, Map<String, String>>) params
                .get("gradeClass");
        Map<String, String> classMap = gradeClassMap.get(gradeMetaMap
                .get(gradeName));
        if (classMap != null && !classMap.containsValue(className)) {
            sb.append("第" + (i) + "行年级下不存在相应的班级，忽略此行数据。<br>");
            logger.debug("第" + (i) + "行年级下不存在相应的班级，忽略此行数据。", i);
            return false;
        }
        Map<String, String> lookUpUser = (Map<String, String>) params
                .get("lookUpUser");
        Map<String, Integer> checkRepeatStudentMap = (Map<String, Integer>) params
                .get("checkRepeatStudentMap");
        // 校验学生是否存在

        if (StringUtils.isNotEmpty(studentName)) {
            if (lookUpUser.get(studentName) == null) {
                sb.append("第" + (i) + "行：学生‘" + studentName
                        + "’不存在或已经被分配到其他班级，忽略此行数据。<br>");
                logger.debug("第" + (i) + "行：学生‘" + studentName
                        + "’不存在或已经被分配到其他班级，忽略此行数据", i);
                return false;
            }
            Integer rowNum = checkRepeatStudentMap.get(studentName);
            if (rowNum != null) {
                sb.append("第" + (i) + "行：学生‘" + studentName + "’与之前的第" + rowNum
                        + "行重复，忽略此行数据。<br>");
                logger.debug("第" + (i) + "行：学生‘" + studentName + "’与之前的第" + rowNum
                        + "行重复，忽略此行数据", i);
                return false;
            } else {
                checkRepeatStudentMap.put(studentName, i);
            }
        }
        return true;
    }

    private Map<String, String> lookUpUser(String orgId, Integer schoolYear) {
        Student s = new Student();
        s.setOrgId(orgId);
        s.setSchoolYear(schoolYear);
        List<Student> uslist = studentService.findStudentsWithoutClass(s); // 根据学校id获取所有没有被分配班级的学生
        Map<String, String> userMap = new HashMap<String, String>();
        for (Student us : uslist) {
            userMap.put(us.getName(), us.getId());
        }
        return userMap;
    }

    @SuppressWarnings("unchecked")
    private String getClassId(Map<ExcelTitle, String> rowValueMap,
                              Map<String, Object> params) {
        String gradeName = rowValueMap.get(ExcelHeader.GRADE);
        String className = rowValueMap.get(ExcelHeader.CLASS);
        Map<Integer, Map<String, String>> gradeClassMap = (Map<Integer, Map<String, String>>) params
                .get("gradeClass");
        Map<String, Integer> greNames = (Map<String, Integer>) params
                .get("gradeMetaMap");
        Map<String, String> classMap = gradeClassMap.get(greNames.get(gradeName)); // 当前年级对应的所有班级
        for (String classId : classMap.keySet()) {
            if (classMap.get(classId).equals(className)) {
                return classId;
            }
        }
        return null;
    }

    /**
     * 获取学校下所有的年级集合
     *
     * @param orgId
     *          机构ID
     * @return 年级集合
     */
    private List<Meta> getAllGradeMetaList(String orgId) {
        PtOrg org = orgMapper.selectById(orgId);
        // 学校的学段-年级map
        Map<Integer, List<Meta>> phasegradeMap = MetaUtils.getOrgTypeProvider()
                .listPhaseXzGradeMap(org.getSchoolings());
        List<Meta> gradeMetaList = new ArrayList<Meta>();
        Collection<List<Meta>> gradeListList = phasegradeMap.values();
        for (List<Meta> gradeListTemp : gradeListList) {
            gradeMetaList.addAll(gradeListTemp);
        }
        return gradeMetaList;
    }

    @Override
    public int sheetIndex() {
        return 0;
    }

    @Override
    public int titleLine() {
        return headline;
    }

    @Override
    protected ExcelTitle[] titles() {
        return ExcelHeader.values();
    }

    /**
     * 处理标题内容，去掉空白字符，去掉* 号 子类覆盖做特殊处理
     *
     */

    @Override
    protected String prepareTitle(String name) {
        name = super.prepareTitle(name);
        return name != null && name.length() > 4 ? name.replaceAll("\\(必填\\)", "")
                : name;
    }

    /**
     * 判定模板是否设置标题列为必填
     *
     */
    @Override
    protected boolean isRequiredColumn(String columnName) {
        if (columnName != null) {
            return columnName.contains("必填");
        }
        return super.isRequiredColumn(columnName);
    }

}
